/**
* file name  : Jenoware/it/ciano/jenoware/CsvHead.java
* authors    : Luciano Xumerle
* created    : sab 18 dic 2004 17:36:46 CET
*
* Copyright (c) 2004-2006 Luciano Xumerle. All rights reserved.
*
* This file is part of gephinfo.
*
* gephinfo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
*/

package it.ciano.jenoware;

import java.util.HashMap;
import java.util.ArrayList;

/**
 * The class extends HashMap and manages the set of field names in a CSV file.
 * The HashMap keys are the field names and the values are the field position
 * in the CSV file (the column number starting by zero).
 *
 * @author Luciano Xumerle
 * @version 0.0.1
 */
final public class CsvHead extends HashMap {
    static final long serialVersionUID = -5974631410152692252L;

    private ArrayList _geno;
    private ArrayList _pheno;
    private String _fixedFields[];

    /**
      * Contains the names of the fixed fields of a pedigree file:
      * the family pedigree field name, the subject pedigree field name,
      * the father pedigree field name, the mother pedigree field name,
      * the sex field name, the proband field name,
      * and the unique identifier field name.
      */
    public final static String[] fixedFields =
    { "fml", "sbj", "fth", "mth", "sex", "prb", "id" };

    /**
     * Empty Class Constructor.
     */
    public CsvHead() {
        super();
    }

    /**
     * Class Constructor.
     * The list provided by the public fixedFields array is used.
     *
     * @param a the list of fields of the CSV file.
     */
    public CsvHead ( String[] a ) {
        this ( a, null );
    }

    /**
     * Class Constructor.
     *
     * @param a the list of fields of the CSV file.
     * @param userFixedFields the names of the fixed fields provided by user.
     */
    public CsvHead ( String[] a, String[] userFixedFields ) {
        super();
        _geno = new ArrayList();
        _pheno = new ArrayList();
        // set the fixed field array ...
        if ( userFixedFields != null && userFixedFields.length == fixedFields.length )
            _fixedFields = userFixedFields;
        else
            _fixedFields = fixedFields;
        // ... and initialize the positions
        for ( int j = 0; j < _fixedFields.length; j++ )
            put ( _fixedFields[ j ], new Integer ( -1 ) );

        for ( int i = 0; i < a.length; i++ ) {
            String h = a[i].toLowerCase();
            for ( int j = 0; j < _fixedFields.length; j++ ) {
                if ( h.equals ( _fixedFields[j].toLowerCase() )  )
                    put ( _fixedFields[ j ], new Integer ( i ) );
            }
        }

        // parse the input header array
        // the previous element is used to detect
        // genotypes stored with two columns named:
        // <marker>_1 and <marker>_2
        if ( a != null ) {
            this.put ( a[ 0 ], new Integer ( 0 ) );
            String prev = a[ 0 ];
            String prevtail = a[ 0 ].substring ( a[ 0 ].length() - 2 );
            for ( int i = 1; i < a.length; i++ ) {
                this.put ( a[ i ], new Integer ( i ) );
                String curtail = a[ i ].substring ( a[ i ].length() - 2 );
                // if the current column complete a genotype then there is
                // no need to update the previous element and the previous
                // tail element because the genotype is stored with suffix _1.
                if ( curtail.equals ( "_2" ) && prevtail.equals ( "_1" ) ) {
                    String a1 = a[ i ].substring ( 0, a[ i ].length() - 2 );
                    String a2 = prev.substring ( 0, prev.length() - 2 );
                    if ( a1.equals ( a2 ) ) {
                        _geno.add ( prev );
                        continue;
                    }
                }
                if ( !isFixedField ( prev ) && !isParsed ( prev ) )
                    _pheno.add ( prev );
                prev = a[ i ];
                prevtail = curtail;
            }
            if ( !isFixedField ( prev ) && !isParsed ( prev ) )
                _pheno.add ( prev );
        }
    }

    /**
     * Returns the fields name contained in the CSV file.
     *
     * @return the list of fields name.
     */
    public String[] getHead() {
        ArrayList a = new ArrayList();
        for ( int j = 0; j < _fixedFields.length; j++ )
            a.add ( _fixedFields[ j ] );
        a.addAll ( _pheno );
        a.addAll ( _geno );
        String rr[] = new String[ a.size() ];
        rr = ( String[] ) a.toArray ( rr );
        return rr;
    }

    /**
     * Returns list of phenotype fields name.
     *
     * @return a String array.
     */
    public String[] getPhenotypeList() {
        if ( _pheno.size() == 0 )
            return null;
        String rr[] = new String[ _pheno.size() ];
        return ( String[] ) _pheno.toArray ( rr );
    }

    /**
     * Returns the list of genotype fields name.
     *
     * @return a String array.
     */
    public String[] getGenotypeList() {
        if ( _geno.size() == 0 )
            return null;
        String rr[] = new String[ _geno.size() ];
        return ( String[] ) _geno.toArray ( rr );
    }

    /**
     * Returns the position of a given field name.
     *
     * @param field the field name.
     * @return The position (-1 if field name isn't present).
     */
    public int getIndex ( String field ) {
        if ( this.containsKey ( field ) )
            return ( ( Integer ) this.get ( field ) ).intValue();
        return -1;
    }

    /**
     * Returns position of "id" field.
     *
     * @return the int position.
     */
    public int posID() {
        return getIndex ( _fixedFields[ 6 ] );
    }

    /**
     * Returns position of "fml" field.
     *
     * @return the int position.
     */
    public int posFML() {
        return getIndex ( _fixedFields[ 0 ] );
    }

    /**
      * Returns position of "sbj" field.
      *
      * @return the int position.
      */
    public int posSBJ() {
        return getIndex ( _fixedFields[ 1 ] );
    }

    /**
     * Returns position of "fth" field.
     *
     * @return the int position.
     */
    public int posFTH() {
        return getIndex ( _fixedFields[ 2 ] );
    }

    /**
     * Returns position of "mth" field.
     *
     * @return the int position.
     */
    public int posMTH() {
        return getIndex ( _fixedFields[ 3 ] );
    }

    /**
     * Returns position of "sex" field.
     *
     * @return the int position.
     */
    public int posSEX() {
        return getIndex ( _fixedFields[ 4 ] );
    }

    /**
     * Returns position of "prb" field.
     *
     * @return the int position.
     */
    public int posPRB() {
        return getIndex ( _fixedFields[ 5 ] );
    }

    /**
     * Overrides toString method.
     *
     * @return String object.
     */
    public String toString() {
        String out = "Pedigree fields:\n";
        for ( int j = 0; j < _fixedFields.length; j++ )
            if ( getIndex ( _fixedFields[ j ] ) > -1 )
                out += " -> " + _fixedFields[ j ] + "\n";
        out += "Phenotype fields:\n";
        for ( int j = 0; j < _pheno.size(); j++ )
            if ( getIndex ( ( String ) _pheno.get ( j ) ) > -1 )
                out += " -> " + ( String ) _pheno.get ( j ) + "\n";
        out += "Genotype fields:\n";
        for ( int j = 0; j < _geno.size(); j++ )
            if ( getIndex ( ( String ) _geno.get ( j ) ) > -1 )
                out += " -> " + ( String ) _geno.get ( j ) + "\n";
        return out;
    }

    /**
     * Returns true if the field is contained in the fixed field array.
     *
     * @param curr the field name.
     * @return true or false.
     */
    public boolean isFixedField ( String curr ) {
        for ( int j = 0; j < _fixedFields.length; j++ )
            if ( curr.toLowerCase().equals ( _fixedFields[ j ] ) )
                return true;
        return false;
    }

    /**
     * Returns true if the field is contained in the list of detected phenotypes.
     *
     * @param curr the phenotype field name.
     * @return true or false.
     */
    public boolean isPheno ( String curr ) {
        for ( int j = 0; j < _pheno.size(); j++ )
            if ( ( ( String ) _pheno.get ( j ) ).equals ( curr ) )
                return true;
        return false;
    }

    /**
     * Returns true if the field is contained in the list of detected genotypes.
     *
     * @param curr the genotype field name.
     * @return true or false.
     */
    private boolean isGeno ( String curr ) {
        for ( int j = 0; j < _geno.size(); j++ )
            if ( ( ( String ) _geno.get ( j ) ).equals ( curr ) )
                return true;
        return false;
    }

    /**
     * Returns true if the field is contained in this CVS header.
     *
     * @param curr the field name.
     * @return true or false.
     */
    public boolean isParsed ( String curr ) {
        return ( isGeno ( curr ) || isPheno ( curr ) || isFixedField ( curr ) );
    }


    /**
     * Insert the values of a given Sbj's row (from database or csv file) in the Pedigree object.
     * Used internally by methods Pedigree.addCSV() and PgSQL.updatePedigree().
     *
     * @param row the String array.
     * @param head the CsvHead object.
     */
    public void addRow ( Pedigree ped, String[] row ) {
        addRow ( ped, row, false );
    }


    /**
     * Insert the values of a given Sbj's row (from database or csv file) in the Pedigree object.
     * Used internally by methods Pedigree.addCSV() and PgSQL.updatePedigree().
     *
     * @param row the String array.
     * @param head the CsvHead object.
     * @param mantainAllelesOrder the genotype object stores the alleles order as found in row list.
     */
    public void addRow ( Pedigree ped, String[] row, boolean mantainAllelesOrder ) {
        String id = "0";
        if ( posID() > -1 && posID() <row.length ) {
            id = row[ posID() ];
            String fml = "";
            String sbj = "";
            String fth = "";
            String mth = "";
            String sex = "";
            String prb = "";
            if ( posFML() > -1 )
                fml = row[ posFML() ];
            if ( posSBJ() > -1 )
                sbj = row[ posSBJ() ];
            if ( posFTH() > -1 )
                fth = row[ posFTH() ];
            if ( posMTH() > -1 )
                mth = row[ posMTH() ];
            if ( posSEX() > -1 )
                sex = row[ posSEX() ];
            if ( posPRB() > -1 )
                prb = row[ posPRB() ];
            Sbj ss = new Sbj ( id, fml, sbj, fth, mth, sex, prb );

            // phenotype finded
            String[] pheno = getPhenotypeList();
            if ( pheno != null )
                for ( int i = 0; i < pheno.length; i++ ) {
                    ped.addPhenotype2List ( pheno[ i ] );
                    int phenoIndex = getIndex ( pheno[ i ] );
                    if ( phenoIndex < row.length )
                        ss.addPhenotype ( pheno[ i ], row[ phenoIndex ] );
                }

            // genotype finded
            String[] geno = getGenotypeList();
            if ( geno != null )
                for ( int i = 0; i < geno.length; i++ ) {
                    int index = getIndex ( geno[ i ] );
                    Allele allele1 = null;
                    Allele allele2 = null;
                    String nn = geno[ i ].substring ( 0, geno[ i ].length() - 2 );
                    ped.addMarker2List ( nn );
                    if ( row.length > ( index + 1 ) ) {
                        allele1 = new Allele ( row[ index ] );
                        allele2 = new Allele ( row[ index + 1 ] );
                    }
                    // we use the full genotype constructor to set the order of alleles
                    // and frequenzcies==1 because there is only a possible Genotype.
                    Genotype gg=new Genotype ( nn, allele1, allele2, "", 1.0, mantainAllelesOrder );
                    ss.addGenotype ( gg );
                }
            ped.addSbj ( ss );
        }
    }


} // end class

